/**
* @Auther:gy
* @Date:2021/8/11 14:43
 */

package registry

import (
	"errors"
	"gitee.com/yanggit123/tool/module"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/model"
	"github.com/nacos-group/nacos-sdk-go/vo"
	"github.com/spf13/cast"
	"math"
	"strings"
	"sync"
	"time"
)

func NewNacosClient() *NacosClient {
	return &NacosClient{
		nacosMap:      make(map[string][]model.SubscribeService, 0),
		nacosCountMap: make(map[string]int, 0),
		exit:          make(chan bool, 0),
	}
}

const (
	defaultGroup       = "DEFAULT_GROUP"
	defaultClusterName = "DEFAULT"
)

type NacosClient struct {
	client        naming_client.INamingClient
	nacosMap      map[string][]model.SubscribeService
	nacosCountMap map[string]int
	exit          chan bool
	rw            sync.RWMutex
	rwcount       sync.RWMutex
}

func (nc *NacosClient) NacosRegister(serviceName string, config module.NacosConf) error {
	clientConfig := constant.ClientConfig{
		NamespaceId:         config.NamespaceID, // 如果需要支持多namespace，我们可以场景多个client,它们有不同的NamespaceId
		TimeoutMs:           5000,
		NotLoadCacheAtStart: true,
		LogDir:              "./log",
		CacheDir:            "./cache",
		LogLevel:            "debug",
	}
	// 至少一个ServerConfig
	serverConfigs := []constant.ServerConfig{}
	for _, v := range config.Address {
		strs := strings.Split(v, ":")
		serverConfig := constant.ServerConfig{
			IpAddr:      strs[0],
			ContextPath: "/nacos",
			Port:        cast.ToUint64(strs[1]),
		}
		serverConfigs = append(serverConfigs, serverConfig)
	}

	// 创建服务发现客户端
	namingClient, err := clients.CreateNamingClient(map[string]interface{}{
		"serverConfigs": serverConfigs,
		"clientConfig":  clientConfig,
	})
	if err != nil {
		return err
	}

	success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{
		Ip:          config.ServiceIP,
		Port:        config.ServicePort,
		ServiceName: serviceName,
		Weight:      10,
		Enable:      true,
		Healthy:     true,
		Ephemeral:   true,
		Metadata:    map[string]string{},
		ClusterName: defaultClusterName, // default value is DEFAULT
		GroupName:   defaultGroup,       // default value is DEFAULT_GROUP
	})
	if err != nil {
		return err
	}
	if !success {
		return errors.New("register fail")
	}
	nc.client = namingClient
	go nc.startWatch(config.NamespaceID)
	return nil
}
func (nc *NacosClient) DeregisterRegister(serviceName string, config module.NacosConf) {
	nc.client.DeregisterInstance(vo.DeregisterInstanceParam{
		Ip:          config.ServiceIP,
		Port:        config.ServicePort,
		ServiceName: serviceName,
		Ephemeral:   true,
		Cluster:     defaultClusterName, // default value is DEFAULT
		GroupName:   defaultGroup,       // default value is DEFAULT_GROUP
	})
	nc.exit <- true
}
func (nc *NacosClient) startWatch(NamespaceID string) {
	for {
		services := nc.GetAllServicesInfo(NamespaceID)
		for _, v := range services {
			if _, ok := nc.nacosMap[v]; !ok {
				nc.Subscribe(v)
			}
		}
		select {
		case <-time.After(time.Minute * 10):
		case <-nc.exit:
			return
		}
	}
}

// 简易的负载均衡
func (nc *NacosClient) GetIpPortByName(serviceName string) string {
	if _, ok := nc.nacosCountMap[serviceName]; !ok {
		nc.Subscribe(serviceName)
	}
	service := nc.nacosMap[serviceName]
	nc.rwcount.Lock()
	count := nc.nacosCountMap[serviceName]
	nc.nacosCountMap[serviceName] = count + 1
	nc.rwcount.Unlock()
	if len(service) == 0 {
		return ""
	}
	index := count % len(service)
	return service[index].Ip + ":" + cast.ToString(service[index].Port)
}
func (nc *NacosClient) GetAllServicesInfo(NamespaceID string) []string {
	serviceList, _ := nc.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
		NameSpace: NamespaceID,
		GroupName: "DEFAULT_GROUP",
		PageNo:    1,
		PageSize:  math.MaxInt32,
	})
	return serviceList.Doms
}

// [{ClusterName:DEFAULT Enable:true InstanceId: Ip:127.0.0.1 Metadata:map[] Port:3017 ServiceName:DEFAULT_GROUP@@test Valid:false Weight:10}]
// [{ClusterName:DEFAULT Enable:true InstanceId:127.0.0.1#3017#DEFAULT#DEFAULT_GROUP@@test Ip:127.0.0.1 Metadata:map[] Port:3017 ServiceName:DEFAULT_GROUP@@test Valid:false Weight:10}]
func (nc *NacosClient) Subscribe(serviceName string) {
	nc.client.Subscribe(&vo.SubscribeParam{
		ServiceName: serviceName,
		GroupName:   defaultGroup,                 // default value is DEFAULT_GROUP
		Clusters:    []string{defaultClusterName}, // default value is DEFAULT
		SubscribeCallback: func(services []model.SubscribeService, err error) {
			nc.rw.Lock()
			nc.nacosMap[serviceName] = services
			nc.rw.Unlock()
		},
	})
}
